package com.netty.demo.server.netty.tcp;

import com.netty.demo.server.netty.config.NettyConfig;
import com.netty.demo.server.netty.model.NettyNetwork;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.util.concurrent.Future;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import javax.annotation.PreDestroy;
import javax.annotation.Resource;
import java.util.List;

/**
 * netty服务类
 *
 * @author yxl 余秀良
 * @version 1.0
 * <p>date: 2019年09月07日  下午4:30 </p>
 * <p>address: 四川成都 </p>
 */
@Component
@Slf4j
public class NettyTcpServer {
    /**
     * 处理Accept连接事件的线程，这里线程数设置为1即可，netty处理链接事件默认为单线程，过度设置反而浪费cpu资源
     */
    private final EventLoopGroup bossGroup = new NioEventLoopGroup(1);
    /**
     * 处理handler的工作线程，其实也就是处理IO读写 。线程数据默认为 CPU 核心数乘以2
     */
    private final EventLoopGroup workerGroup = new NioEventLoopGroup();
    @Resource
    private NettyTcpServerInitializer nettyServerInitializer;

    /**
     * netty服务启动类
     *
     * @author 余秀良
     * @version 1.0
     * <p>  date: 2019 - 09 - 07  16:32    </p>
     * <p>  address: 四川成都  </p>
     */
    public void start() {
        try {
            //创建ServerBootstrap实例
            ServerBootstrap serverBootstrap = new ServerBootstrap();
            //初始化ServerBootstrap的线程组
            serverBootstrap.group(bossGroup, workerGroup);
            //设置将要被实例化的ServerChannel类
            serverBootstrap.channel(NioServerSocketChannel.class);
            //开启广播
            serverBootstrap.option(ChannelOption.SO_BROADCAST, true);
            //重复地址
            serverBootstrap.option(ChannelOption.SO_REUSEADDR, true);
            //在ServerChannelInitializer中初始化ChannelPipeline责任链，并添加到serverBootstrap中
            serverBootstrap.childHandler(nettyServerInitializer);
            //标识当服务器请求处理线程全满时，用于临时存放已完成三次握手的请求的队列的最大长度
            serverBootstrap.option(ChannelOption.SO_BACKLOG, 1024);
            // 是否启用心跳保活机机制
            serverBootstrap.childOption(ChannelOption.SO_KEEPALIVE, true);
            //TODO 如果发送接收超长数据 则需要放开该代码  并且设置最大长度
            ///int max = xxx;
            ///bootstrap.option(ChannelOption.RCVBUF_ALLOCATOR, new FixedRecvByteBufAllocator(max));
            bindIpAndPort(serverBootstrap);
        } catch (Exception e) {
            log.error("netty服务启动异常{}{}", e.getLocalizedMessage(), e.getMessage());
            e.printStackTrace();
        }
    }

    /**
     * 绑定服务的ip和端口
     *
     * @param bootstrap Bootstrap
     */
    private void bindIpAndPort(ServerBootstrap bootstrap) {
        List<NettyNetwork> nettyNetworkList = NettyConfig.getServerConfig();
        if (nettyNetworkList != null && nettyNetworkList.size() > 0) {
            for (NettyNetwork nettyNetwork : nettyNetworkList) {
                int port = Integer.parseInt(nettyNetwork.getMonitorPort());
                //设置监听端口 开启监听
                ChannelFuture channelFuture = bootstrap.bind(port);
                addListener(port, nettyNetwork.getMonitorIp(), channelFuture);
            }
        }
    }

    /**
     * @param port          端口号
     * @param ip            ip地址
     * @param channelFuture ChannelFuture
     */
    private void addListener(int port, String ip, ChannelFuture channelFuture) {
        channelFuture.addListener(future1 -> {
            if (future1 != null && future1.isSuccess()) {
                log.info("netty服务启动成功,ip端口: {} {}", ip, port);
            } else {
                log.error("netty服务启动失败,ip端口: {} {}", ip, port);
                System.err.println("netty服务启动失败,ip: " + ip + "端口:  " + port);
                assert future1 != null;
                future1.cause().printStackTrace();
            }
        });
    }

    /**
     * 服务关闭
     */
    private synchronized void stopServer() {
        try {
            Future<?> future = this.workerGroup.shutdownGracefully().await();
            if (!future.isSuccess()) {
                log.error("workerGroup 无法正常停止", future.cause());
            }

            future = this.bossGroup.shutdownGracefully().await();
            if (!future.isSuccess()) {
                log.error("bossGroup 无法正常停止", future.cause());
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        log.info("TCP服务已经停止...");
    }

    /**
     * 停止udp server服务
     * 销毁前的拦截
     */
    @PreDestroy
    public void destroy() {
        stopServer();
    }
}
